package anveshitha.indexers;

import static anveshitha.config.AnveshithaConstants.JAVA;
import static anveshitha.config.AnveshithaConstants.indexFileCounter;

import java.io.File;

import org.apache.log4j.Logger;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexWriter;

import anveshitha.analyzers.JavaSourceCodeAnalyzer;
import anveshitha.config.AnveshithaConstants;
import anveshitha.config.AnveshithaConstants.FieldEnum;

import com.thoughtworks.qdox.JavaDocBuilder;
import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.JavaMethod;
import com.thoughtworks.qdox.model.JavaParameter;
import com.thoughtworks.qdox.model.JavaSource;
import com.thoughtworks.qdox.model.Type;

/**
 * This class indexes Java Source Files
 * 
 * @author varun
 */
public class JavaSourceCodeIndexer extends SourceCodeIndexer {
	
	private static Logger log = Logger.getLogger(JavaSourceCodeIndexer.class);

	public JavaSourceCodeIndexer(boolean recursive) {

		super(JAVA, AnveshithaConstants.JAVA_SRC_DIRS, recursive, new JavaSourceCodeAnalyzer());
	}

	@Override
	public void indexFile(IndexWriter indexWriter, File file) throws Exception {
		Document luceneDoc = new Document();

		// parse the file
		JavaDocBuilder javaDocBuilder = new JavaDocBuilder();
		javaDocBuilder.addSource(file);

		// Get the sources
		JavaSource javaSources[] = javaDocBuilder.getSources();

		// iterate through the sources in the file and index them
		for (int index = 0; index < javaSources.length; index++) {
			JavaSource javaSource = javaSources[index];

			// -----------INDEXING RULES----------
			// Field Type
			// ----------------------------------
			// Class Name Text (Stored,Tokenized)
			// Import Declarations Keyword (Stored,Indexed)
			// Method Name Text
			// Method Block (Code) UnStored (Not Stored, Tokenized)
			// Method Parameter Type Text
			// Return Type Text
			// Comments UnStored
			// Extends Class Text
			// Implements Text
			// Package Name UnIndexed
			// File Path Indexed

			luceneDoc.add(new Field(FieldEnum.FILEPATH.name(), file
					.getAbsolutePath(), Field.Store.YES, Field.Index.UN_TOKENIZED));
			luceneDoc.add(new Field(FieldEnum.PACKAGENAME.name(), javaSource
					.getPackage(), Field.Store.YES, Field.Index.UN_TOKENIZED));

			String[] imports = javaSource.getImports();
			for (String s : imports) {
				luceneDoc.add(new Field(FieldEnum.IMPORT.name(), s,
						Field.Store.YES, Field.Index.TOKENIZED));
			}

			JavaClass javaClasses[] = javaSource.getClasses();

			for (JavaClass javaClass : javaClasses) {
				indexClass(luceneDoc, javaClass);
			}

		}

		indexWriter.addDocument(luceneDoc);
	}

	/*
	 * (non-Javadoc)
	 * @see java.util.TimerTask#run()
	 */
	@Override
	public void run() {
		try {
			log.info("Starting indexing of Java Files....");
			long start = System.currentTimeMillis();
			index();
			long end = System.currentTimeMillis();
			log.info("Done indexing of " + indexFileCounter
					+ " files in ~" + ((end - start) / 1000F) + " sec");
		} catch (Exception e) {
			log.error(e.getMessage());
		}
	}

	private void indexClass(Document luceneDoc, JavaClass javaClass) {
		// class name
		luceneDoc.add(new Field(FieldEnum.CLASSNAME.name(),
				javaClass.getName(), Field.Store.YES, Field.Index.TOKENIZED));

		// super class name
		if(null != javaClass.getSuperClass())
		luceneDoc.add(new Field(FieldEnum.EXTENDS.name(), javaClass
				.getSuperClass().getValue(), Field.Store.YES,
				Field.Index.TOKENIZED));

		// implemented interfaces
		Type[] types = javaClass.getImplements();
		for (Type type : types)
			luceneDoc.add(new Field(FieldEnum.IMPLEMENTS.name(), type
					.getValue(), Field.Store.YES, Field.Index.TOKENIZED));

		// methods
		addMethodFields(luceneDoc, javaClass.getMethods());

		// comments
		if (null != javaClass.getComment())
			luceneDoc.add(new Field(FieldEnum.COMMENTS.name(), javaClass
					.getComment(), Field.Store.NO, Field.Index.TOKENIZED));

		// check for nested classes
		JavaClass[] javaClasses = javaClass.getNestedClasses();
		for (JavaClass nestedClass : javaClasses)
			indexClass(luceneDoc, nestedClass);
	}

	private static void addMethodFields(Document luceneDoc,
			JavaMethod[] javaMethods) {

		for (JavaMethod method : javaMethods) {

			// method name
			luceneDoc.add(new Field(FieldEnum.METHODNAME.name(), method
					.getName(), Field.Store.YES, Field.Index.TOKENIZED));

			// method code
			luceneDoc.add(new Field(FieldEnum.METHODCODE.name(), method
					.getSourceCode(), Field.Store.NO, Field.Index.TOKENIZED));

			// return type
			if (null != method.getReturns())
				luceneDoc.add(new Field(FieldEnum.RETURNTYPE.name(), method
						.getReturns().getValue(), Field.Store.NO,
						Field.Index.TOKENIZED));

			JavaParameter[] parameters = method.getParameters();
			for (JavaParameter param : parameters)
				luceneDoc.add(new Field(FieldEnum.METHODPARAMTYPE.name(), param
						.getType().getValue(), Field.Store.NO,
						Field.Index.TOKENIZED));
		}
	}

	/**
	 * Use for testing purposes
	 * @param args
	 */
	public static void main(String[] args) {
		try {
			AnveshithaConstants.loadConfigData("");
			JavaSourceCodeIndexer jsi = new JavaSourceCodeIndexer(true);
			System.out.println("Starting indexing....");
			long start = System.currentTimeMillis();
			jsi.index();
			long end = System.currentTimeMillis();
			System.out.println("Done indexing of " + indexFileCounter
					+ " files in ~" + ((end - start) / 1000F) + " sec");
		} catch (Exception e) {
			System.err.println(e.getMessage());
		}
	}

}
